unit Location;

interface

uses
  GlobalRecordsAndEnums,
  Classes;

type
  TLocation = class
    private
      _name: String;
      _id: Integer;
      _materialmodifier: Double;
      _timemodifier: Double;
      _activity: EActivity;
    public
      constructor Create; overload;
      constructor Create(location: SLocation); overload;

      procedure Load(location: SLocation);
      function Save: SLocation;

      property Name: String read _name;
      property ID: Integer read _id;
      property MaterialModifier: Double read _materialmodifier;
      property TimeModifier: Double read _timemodifier;
      property Activity: EActivity read _activity;
  end;
  TLocationList = array of TLocation;

  TLocations = class
    private
      _locations: TList;

      function LoadLocations_v041(stream: TMemoryStream): Boolean;
      function GetLocationCount: Integer;
    public
      constructor Create;
      destructor Destroy;

      procedure Add(location: TLocation);

      function GetLocationByID(id: Integer): TLocation;
      function GetLocation(index: Integer): TLocation;
      function GetLocationByActivity(activity: EActivity): TLocationList;

      procedure Clear;

      property Count: Integer read GetLocationCount;
  end;
  function SortLocation(Item1, Item2: Pointer): Integer;

implementation

uses
  SysUtils, Dialogs, Forms;

//=======================
//====== TLocation ======
//=======================

constructor TLocation.Create;
begin
  _name := 'New Location';
  _id := -1;
  _materialmodifier := 1;
  _timemodifier := 1;
  _activity := ACT_None;
end;

constructor TLocation.Create(location: SLocation);
begin
  _name := location.Name;
  _id := location.ID;
  _materialmodifier := location.MaterialModifier;
  _timemodifier := location.TimeModifier;
  _activity := location.Activity;
end;

procedure TLocation.Load(location: SLocation);
begin
  _name := location.Name;
  _id := location.ID;
  _materialmodifier := location.MaterialModifier;
  _timemodifier := location.TimeModifier;
  _activity := location.Activity;
end;

function TLocation.Save: SLocation;
begin
  result.Name := _name;
  result.ID := _id;
  result.MaterialModifier := _materialmodifier;
  result.TimeModifier := _timemodifier;
  result.Activity := _activity;
end;

//========================
//====== TLocations ======
//========================

function TLocations.GetLocationCount: Integer;
begin
  result := _locations.Count
end;

constructor TLocations.Create;
var
  stream: TMemoryStream;
  version: Double;
begin
  _locations := TList.Create;
  if (not FileExists('res\Locations.dat')) then begin
    MessageDlg('Unable to locate the file ' + ExtractFileDir(Application.ExeName) + '\res\Locations.dat.', mtError, [mbOk], 0);
  end
  else begin
    stream := TMemoryStream.Create;
    stream.LoadFromFile('res\Locations.dat');
    stream.Position := 0;
    try
      stream.Read(version, sizeof(Double));
    except
      MessageDlg('Error reading Locations.dat: invalid version information.', mtError, [mbOk], 0);
      stream.Free;
      exit;
    end;
    if (Round(version * 100) = 41) then // just 0.41 seems to result in a precision error
      self.LoadLocations_v041(stream)
    else begin
      MessageDlg('Error reading Locations.dat: no suitable load function found.', mtError, [mbOk], 0);
    end;
    stream.Free;
    _locations.Sort(SortLocation);
  end;
end;

destructor TLocations.Destroy;
var
  i: Integer;
  stream: TMemoryStream;
  loc: SLocation;
  version: Double;
begin
  if (adminhax) then begin
    version := 0.41;
    stream := TMemoryStream.Create;
    stream.Write(version, SizeOf(Double));
    for i := 0 to _locations.Count -1 do begin
      loc := TLocation(_locations[i]).Save;
      stream.Write(loc, SizeOf(SLocation));
    end;
    try
      stream.SaveToFile('res\Locations.dat');
    except
      on e: Exception do MessageDlg(e.Message, mtError, [mbOk], 0);
    end;
    stream.Free;
  end;


  for i := 0 to _locations.Count -1 do begin
    TLocation(_locations.Items[i]).Destroy;
  end;
  _locations.Destroy;
  
  inherited Destroy;
end;

function TLocations.LoadLocations_v041(stream: TMemoryStream): Boolean;
var
  loc: SLocation;
begin
  result := true;
  while stream.Position < stream.Size do begin
    try
      stream.Read(loc, sizeof(SLocation));
      _locations.Add(TLocation.Create(loc));
    except
      MessageDlg('Error reading Locations.dat: invalid location', mtError, [mbOk], 0);
      result := false;
      exit;
    end;
  end;
end;

function SortLocation(Item1, Item2: Pointer): Integer;
begin
  result := CompareText(TLocation(Item1).Name, TLocation(Item2).Name);
end;

procedure TLocations.Add(location: TLocation);
begin
  _locations.Add(location);
end;

function TLocations.GetLocationByID(id: Integer): TLocation;
var
  i: Integer;
begin
  result := nil;
  for i := 0 to _locations.Count -1 do begin
    if (TLocation(_locations[i]).ID = id) then result := TLocation(_locations[i]);
  end;
end;

function TLocations.GetLocation(index: Integer): TLocation;
begin
  if (index < _locations.Count) then result := TLocation(_locations[index])
  else result := nil;
end;

function TLocations.GetLocationByActivity(activity: EActivity): TLocationList;
var
  i, len: Integer;
  location: TLocation;
begin
  SetLength(result, 0);
  for i := 0 to _locations.Count -1 do begin
    location := TLocation(_locations[i]);
    if (location.Activity = activity) then begin
      len := Length(result);
      SetLength(result, len +1);
      result[len] := location;
    end;
  end;
end;

procedure TLocations.Clear;
var
  i: Integer;
begin
  for i := 0 to _locations.Count -1 do begin
    TLocation(_locations[i]).Destroy;
  end;
  _locations.Destroy;

  _locations := TList.Create;
end;

end.
